﻿using System;
using System.Collections.Generic;
using fastCSharp.threading;
using fastCSharp;
using System.Runtime.CompilerServices;
using System.Threading;

namespace fastCSharp.web
{
    /// <summary>
    /// 长连接轮询
    /// </summary>
    public sealed class poll
    {
        /// <summary>
        /// 轮询验证信息
        /// </summary>
        public struct verifyInfo
        {
            /// <summary>
            /// 用户标识
            /// </summary>
            public int UserId;
            /// <summary>
            /// 验证信息
            /// </summary>
            public string Verify;
        }
        /// <summary>
        /// 会话超时
        /// </summary>
        private struct sessionTimeout
        {
            /// <summary>
            /// 超时
            /// </summary>
            public DateTime Timeout;
            /// <summary>
            /// 会话标识
            /// </summary>
            public sessionId SessionId;
            /// <summary>
            /// 新建会话标识
            /// </summary>
            /// <param name="timeout"></param>
            /// <returns></returns>
            public sessionId New(DateTime timeout)
            {
                SessionId.New();
                Timeout = timeout;
                return SessionId;
            }
            /// <summary>
            /// 会话标识验证
            /// </summary>
            /// <param name="verify"></param>
            /// <returns></returns>
            public bool Check(string verify)
            {
                return Timeout > date.NowSecond && SessionId.CheckHex(verify);
            }
            /// <summary>
            /// 清除数据
            /// </summary>
            public void Clear()
            {
                Timeout = DateTime.MinValue;
                SessionId.Null();
            }
        }
        /// <summary>
        /// 长连接轮询验证集合
        /// </summary>
        private sessionTimeout[] sessions = nullValue<sessionTimeout>.Array;
        /// <summary>
        /// 长连接轮询验证访问锁
        /// </summary>
        private readonly object sessionLock = new object();
        /// <summary>
        /// 超时时钟周期
        /// </summary>
        private long timeoutTicks;
        /// <summary>
        /// 超时秒数
        /// </summary>
        /// <param name="timeoutSeconds"></param>
        public poll(int timeoutSeconds = 3600)
        {
            timeoutTicks = fastCSharp.date.SecondTicks * Math.Max(timeoutSeconds, 60);
        }
        /// <summary>
        /// 获取用户长连接轮询验证
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <returns>长连接轮询验证</returns>
        [MethodImpl((MethodImplOptions)fastCSharp.pub.MethodImplOptionsAggressiveInlining)]
        private string getString(int userId)
        {
            sessionId sessionId = get(userId);
            return sessionId.IsNull ? null : sessionId.ToHexString();
        }
        /// <summary>
        /// 获取用户长连接轮询验证
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <returns>长连接轮询验证,0表示失败</returns>
        private sessionId get(int userId)
        {
            if ((uint)userId < (uint)sessions.Length)
            {
                Monitor.Enter(sessionLock);
                sessionId sessionId = sessions[userId].Timeout > date.NowSecond ? sessions[userId].SessionId : sessions[userId].New(date.NowSecond.AddTicks(timeoutTicks));
                Monitor.Exit(sessionLock);
                return sessionId;
            }
            return default(sessionId);
        }
        /// <summary>
        /// 获取用户长连接轮询验证
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <returns>长连接轮询验证</returns>
        [MethodImpl((MethodImplOptions)fastCSharp.pub.MethodImplOptionsAggressiveInlining)]
        public verifyInfo Get(int userId)
        {
            return new verifyInfo { UserId = userId, Verify = userId < 0 ? null : (getString(userId) ?? add(userId)) };
        }
        /// <summary>
        /// 添加用户长连接轮询验证
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        private string add(int userId)
        {
            sessionId sessionId;
            if (userId < sessions.Length)
            {
                Monitor.Enter(sessionLock);
                sessionId = sessions[userId].Timeout > date.NowSecond ? sessions[userId].SessionId : sessions[userId].New(date.NowSecond.AddTicks(timeoutTicks));
                Monitor.Exit(sessionLock);
            }
            else
            {
                Monitor.Enter(sessionLock);
                if (userId < sessions.Length)
                {
                    sessionId = sessions[userId].Timeout > date.NowSecond ? sessions[userId].SessionId : sessions[userId].New(date.NowSecond.AddTicks(timeoutTicks));
                    Monitor.Exit(sessionLock);
                }
                else
                {
                    try
                    {
                        sessionTimeout[] newSessions = new sessionTimeout[Math.Max(sessions.Length << 1, userId + 256)];
                        sessions.CopyTo(newSessions, 0);
                        sessions = newSessions;
                        sessionId = newSessions[userId].New(date.NowSecond.AddTicks(timeoutTicks));
                    }
                    finally { Monitor.Exit(sessionLock); }
                }
            }
            return sessionId.ToHexString();
        }
        /// <summary>
        /// 删除用户长连接轮询验证
        /// </summary>
        /// <param name="userId"></param>
        [MethodImpl((MethodImplOptions)fastCSharp.pub.MethodImplOptionsAggressiveInlining)]
        public void Remove(int userId)
        {
            if ((uint)userId < (uint)sessions.Length)
            {
                Monitor.Enter(sessionLock);
                sessions[userId].Clear();
                Monitor.Exit(sessionLock);
            }
        }
        /// <summary>
        /// 轮询验证检测
        /// </summary>
        /// <param name="verify"></param>
        /// <returns></returns>
        public bool Verify(ref verifyInfo verify)
        {
            return verify.Verify != null && (uint)verify.UserId < (uint)sessions.Length && sessions[verify.UserId].Check(verify.Verify);
        }
        /// <summary>
        /// 长连接轮询
        /// </summary>
        public sealed class dictionary
        {
            /// <summary>
            /// 长连接轮询验证集合
            /// </summary>
            private readonly Dictionary<int, sessionTimeout> sessions = fastCSharp.dictionary.CreateInt<sessionTimeout>();
            /// <summary>
            /// 长连接轮询验证访问锁
            /// </summary>
            private readonly object sessionLock = new object();
            /// <summary>
            /// 超时时钟周期
            /// </summary>
            private long timeoutTicks;
            /// <summary>
            /// 超时秒数
            /// </summary>
            /// <param name="timeoutSeconds"></param>
            public dictionary(int timeoutSeconds = 3600)
            {
                timeoutTicks = fastCSharp.date.SecondTicks * Math.Max(timeoutSeconds, 60);
            }
            /// <summary>
            /// 获取用户长连接轮询验证
            /// </summary>
            /// <param name="userId">用户标识</param>
            /// <returns>长连接轮询验证</returns>
            [MethodImpl((MethodImplOptions)fastCSharp.pub.MethodImplOptionsAggressiveInlining)]
            private string getString(int userId)
            {
                sessionId sessionId = get(userId);
                return sessionId.IsNull ? null : sessionId.ToHexString();
            }
            /// <summary>
            /// 获取用户长连接轮询验证
            /// </summary>
            /// <param name="userId">用户标识</param>
            /// <returns>长连接轮询验证,0表示失败</returns>
            private sessionId get(int userId)
            {
                sessionTimeout value;
                if (sessions.TryGetValue(userId, out value))
                {
                    if (value.Timeout > date.NowSecond) return value.SessionId;
                    else
                    {
                        sessionId sessionId = default(sessionId);
                        sessionId.New();
                        Monitor.Enter(sessionLock);
                        try
                        {
                            sessions[userId] = new sessionTimeout { SessionId = sessionId, Timeout = date.NowSecond.AddTicks(timeoutTicks) };
                        }
                        finally { Monitor.Exit(sessionLock); }
                        return sessionId;
                    }
                }
                return default(sessionId);
            }
            /// <summary>
            /// 获取用户长连接轮询验证
            /// </summary>
            /// <param name="userId">用户标识</param>
            /// <returns>长连接轮询验证</returns>
            [MethodImpl((MethodImplOptions)fastCSharp.pub.MethodImplOptionsAggressiveInlining)]
            public verifyInfo Get(int userId)
            {
                return new verifyInfo { UserId = userId, Verify = getString(userId) ?? add(userId) };
            }
            /// <summary>
            /// 添加用户长连接轮询验证
            /// </summary>
            /// <param name="userId"></param>
            /// <returns></returns>
            private string add(int userId)
            {
                sessionTimeout value;
                if (!sessions.TryGetValue(userId, out value))
                {
                    sessionId sessionId = default(sessionId);
                    sessionId.New();
                    Monitor.Enter(sessionLock);
                    try
                    {
                        if (!sessions.ContainsKey(userId))
                        {
                            sessions.Add(userId, value = new sessionTimeout { SessionId = sessionId, Timeout = date.NowSecond.AddTicks(timeoutTicks) });
                        }
                    }
                    finally { Monitor.Exit(sessionLock); }
                }
                return value.SessionId.ToHexString();
            }
            /// <summary>
            /// 删除用户长连接轮询验证
            /// </summary>
            /// <param name="userId"></param>
            [MethodImpl((MethodImplOptions)fastCSharp.pub.MethodImplOptionsAggressiveInlining)]
            public void Remove(int userId)
            {
                Monitor.Enter(sessionLock);
                sessions.Remove(userId);
                Monitor.Exit(sessionLock);
            }
            /// <summary>
            /// 轮询验证检测
            /// </summary>
            /// <param name="verify"></param>
            /// <returns></returns>
            public bool Verify(ref verifyInfo verify)
            {
                if (verify.Verify != null)
                {
                    sessionTimeout value;
                    return sessions.TryGetValue(verify.UserId, out value) && value.Check(verify.Verify);
                }
                return true;
            }
        }
    }
}
